github.com/aarzilli/tools@v0.0.0-20151123112009-0d27094f75e0/dsu/persistent_cursor/query with cursor.go (about) 1 // Package persistent_cursor shows 2 // how a query iterator has a cursor object; 3 // that can be serialized into memcache 4 // and reused to continue the query in 5 // a different request 6 package persistent_cursor 7 8 import ( 9 "bytes" 10 "fmt" 11 "net/http" 12 "time" 13 14 "appengine" 15 ds "appengine/datastore" 16 "appengine/memcache" 17 18 "github.com/pbberlin/tools/net/http/loghttp" 19 "github.com/pbberlin/tools/stringspb" 20 21 gbp "github.com/pbberlin/tools/dsu/ancestored_gb_entries" // guest book persistence 22 ) 23 24 func guestViewCursor(w http.ResponseWriter, r *http.Request, m map[string]interface{}) { 25 26 c := appengine.NewContext(r) 27 28 q := ds.NewQuery(gbp.GbEntryKind) 29 q.Order("-Date") 30 31 b1 := new(bytes.Buffer) 32 33 cur_start, err := memcache.Get(c, "greeting_cursor") 34 if err == nil { 35 str_curs := string(cur_start.Value) 36 if len(cur_start.Value) > 0 { 37 cursor, err := ds.DecodeCursor(str_curs) // inverse is string() 38 loghttp.E(w, r, err, false) 39 if err == nil { 40 b1.WriteString("found cursor from memcache -" + stringspb.Ellipsoider(str_curs, 10) + "-<br>\n") 41 q = q.Start(cursor) 42 } 43 } 44 } 45 46 iter := q.Run(c) 47 var cntr int = 0 48 for { 49 var g gbp.GbEntryRetr 50 cntr++ 51 if cntr > 2 { 52 b1.WriteString(" batch complete -" + string(cntr) + "-<br>\n") 53 break 54 55 } 56 57 _, err := iter.Next(&g) 58 if err == ds.Done { 59 b1.WriteString("scan complete -" + string(cntr) + "-<br>\n") 60 break 61 } 62 63 if fmt.Sprintf("%T", err) == fmt.Sprintf("%T", new(ds.ErrFieldMismatch)) { 64 err = nil // ignore this one - it's caused by our deliberate differences between gbsaveEntry and gbEntrieRetr 65 } 66 67 if err != nil { 68 b1.WriteString("error fetching next: " + err.Error() + "<br>\n") 69 break 70 } 71 72 b1.WriteString(" - " + g.String()) 73 } 74 75 // Get updated cursor and store it for next time. 76 if cur_end, err := iter.Cursor(); err == nil { 77 78 str_c_end := cur_end.String() // inverse is decode() 79 val := []byte(str_c_end) 80 81 mi_save := &memcache.Item{ 82 Key: "greeting_cursor", 83 Value: val, 84 Expiration: 60 * time.Second, 85 } 86 87 if err := memcache.Set(c, mi_save); err != nil { 88 b1.WriteString("error adding memcache item " + err.Error() + "<br>\n") 89 } else { 90 b1.WriteString("wrote cursor to memcache -" + stringspb.Ellipsoider(str_c_end, 10) + "-<br>\n") 91 } 92 93 } else { 94 b1.WriteString("could not retrieve cursor_end " + err.Error() + "<br>\n") 95 } 96 97 w.Header().Set("Content-Type", "text/html") 98 w.Write(b1.Bytes()) 99 100 w.Write([]byte("<br>----<br>")) 101 102 } 103 104 func init() { 105 http.HandleFunc("/guest-view-cursor", loghttp.Adapter(guestViewCursor)) 106 }